﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
using System.Collections;

namespace WpfLibrary;

public class ItemsControlDragDropDecorator : Decorator
{
    static ItemsControlDragDropDecorator()
    {
        ItemTypeProperty = DependencyProperty.Register("ItemType", typeof(Type), typeof(ItemsControlDragDropDecorator), new FrameworkPropertyMetadata(null));
    }
    public ItemsControlDragDropDecorator() : base()
    {
        AllowDrop = true;
        this.Loaded += new RoutedEventHandler(DraggableItemsControl_Loaded);
    }
    public static readonly DependencyProperty ItemTypeProperty;
    public Type ItemType
    {
        get { return (Type)base.GetValue(ItemTypeProperty); }
        set { base.SetValue(ItemTypeProperty, value); }
    }
    private bool IsFirstLoad = true;
    private bool IsDraging = false;
    private Point DragStartPosition;
    private object? DragData;
    void DraggableItemsControl_Loaded(object sender, RoutedEventArgs e)
    {
        if (!IsFirstLoad)
        {
            return;
        }
        IsFirstLoad = false;
        ItemsControl? itemsControl = Child as ItemsControl;
        if (itemsControl == null)
        {
            return;
        }
        itemsControl.AllowDrop = AllowDrop;
        itemsControl.AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown), true);
        itemsControl.AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp), true);
        itemsControl.AddHandler(MouseMoveEvent, new MouseEventHandler(OnMouseMove), true);
        itemsControl.AddHandler(DropEvent, new DragEventHandler(OnDrop), true);
    }

    private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var itemsControl = sender as ItemsControl;
        if (itemsControl == null)
        {
            return;
        }
        Point p = e.GetPosition(itemsControl);
        DragData = DragUtility.GetDataObjectFromItemsControl(itemsControl, p);
        if (DragData != null)
        {
            DragStartPosition = p;
        }
        e.Handled = true;
    }
    private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        var itemsControl = sender as ItemsControl;
        if (itemsControl == null)
        {
            return;
        }
        ResetState(itemsControl);
        e.Handled = true;
    }
    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        var itemsControl = sender as ItemsControl;
        if (itemsControl == null)
        {
            return;
        }
        if (DragData == null || DragData.GetType() != ItemType)
        {
            return;
        }
        Point currentPosition = e.GetPosition(itemsControl);
        if (e.LeftButton == MouseButtonState.Pressed && !IsDraging
            && ((Math.Abs(currentPosition.X - DragStartPosition.X) > SystemParameters.MinimumHorizontalDragDistance) || (Math.Abs(currentPosition.Y - DragStartPosition.Y) > SystemParameters.MinimumVerticalDragDistance)))
        {
            DataObject dataObject = new DataObject();
            dataObject.SetData("DragData", DragData);
            itemsControl.AllowDrop = false;
            var effects = DragDrop.DoDragDrop(itemsControl, dataObject, DragDropEffects.Copy | DragDropEffects.Move);
            if (effects.HasFlag(DragDropEffects.Move))
            {
                if (itemsControl.ItemsSource != null)
                {
                    IList? list = itemsControl.ItemsSource as IList;
                    if (list != null)
                    {
                        list.Remove(DragData);
                    }
                }
                else
                {
                    itemsControl.Items.Remove(DragData);
                }
            }
            ResetState(itemsControl);
        }
        e.Handled =true;
    }
    private void OnDrop(object sender, DragEventArgs e)
    {
        var itemsControl = sender as ItemsControl;
        if (itemsControl == null)
        {
            return;
        }
        object data = e.Data.GetData("DragData");
        if (data == null || data.GetType() != ItemType)
        {
            return;
        }
        e.Effects = e.KeyStates.HasFlag(DragDropKeyStates.ControlKey) ? DragDropEffects.Copy : DragDropEffects.Move;
        if (itemsControl.ItemsSource != null)
        {
            IList? list = itemsControl.ItemsSource as IList;
            if (list != null && !list.Contains(data))
            {
                list.Add(data);
            }
            else
            {
                e.Effects = DragDropEffects.None;
            }
        }
        else
        {
            if (!itemsControl.Items.Contains(data))
            {
                itemsControl.Items.Add(data);
            }
            else
            {
                e.Effects = DragDropEffects.None;
            }
        }
        e.Handled = true;
    }
    private void ResetState(ItemsControl itemsControl)
    {
        IsDraging = false;
        DragData = null;
        itemsControl.AllowDrop = true;
    }
}
